package com.legal.controller.user;

import com.legal.domain.dto.LoginFormDTO;
import com.legal.domain.dto.RegisterFormDTO;
import com.legal.domain.result.Result;
import com.legal.domain.vo.UserLoginVO;
import com.legal.domain.vo.UserRegisterVO;
import com.legal.enums.RegisterExceptionMsgEnums;
import com.legal.exception.RegisterException;
import com.legal.service.user.CaptchaService;
import com.legal.service.user.IUserService;
import com.legal.service.user.RateLimitService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;

/**
 * <p>
 * 用户信息表 前端控制器
 * </p>
 *
 * @author xiaopeng
 * @since 2025-06-20
 */
@Tag(name = "用户信息表") // OpenAPI 3.0 注解
@RestController
@RequestMapping("/user")
@CrossOrigin
@RequiredArgsConstructor
public class UserController {
    private final IUserService userService;

    private final CaptchaService captchaService;

    private final RateLimitService rateLimitService;

    /**
     * 登录
     *
     * @param loginFormDTO
     * @return
     */
    @Operation(summary = "登录") // OpenAPI 3.0 注解
    @PostMapping("/login")
    public Result<UserLoginVO> login(@RequestBody @Validated LoginFormDTO loginFormDTO) {
        UserLoginVO userLoginVO = userService.login(loginFormDTO);
        return Result.ok(userLoginVO);
    }

    /**
     * 注册接口
     * 提供用户注册功能，包括IP、设备和手机号级别的限流以防止恶意注册行为
     *
     * @param registerDTO 注册信息
     * @param request     请求对象
     * @return
     */
    @Operation(summary = "注册")
    @PostMapping("/register")
    public Result<UserRegisterVO> register(@RequestBody @Validated RegisterFormDTO registerDTO, HttpServletRequest request) {
        // 获取用户请求的IP地址和设备ID，用于后续的限流判断
        String ip = request.getRemoteAddr();
        String deviceId = request.getHeader("Device-Id");
        String phoneNumber = registerDTO.getPhoneNumber();

        // 1. IP级别滑动窗口限流: 5分钟最多3次注册尝试
        // 防止同一IP地址在短时间内进行大量注册尝试
        if (!rateLimitService.checkSlidingRateLimit("register:ip:" + ip, 3, Duration.ofMinutes(5))) {
            throw new RegisterException(RegisterExceptionMsgEnums.REGISTER_ERROR);
        }

        // 2. 设备级别限流: 1小时最多5次注册尝试
        // 针对特定设备的注册尝试进行限制，避免同一设备滥用注册功能
        if (deviceId != null && !rateLimitService.checkSlidingRateLimit(
                "register:device:" + deviceId, 5, Duration.ofHours(1))) {
            throw new RegisterException(RegisterExceptionMsgEnums.REGISTER_EXCEED_LIMIT_EQUIPMENT);
        }

        // 3. 手机号级别限流: 24小时最多3次注册尝试（仅peek检查）
        // 检查手机号的注册尝试次数，以防止恶意尝试，但不立即计数
        if (!rateLimitService.peekSlidingRateLimit(
                "register:phone:" + phoneNumber, 3, Duration.ofHours(24))) {
            throw new RegisterException(RegisterExceptionMsgEnums.REGISTER_EXCEED_LIMIT_PHONE_NUMBER);
        }

        try {
            // 4. 调用业务服务进行注册，此处不进行计数以避免误伤合法用户
            UserRegisterVO userRegisterVO = userService.register(registerDTO);

            // 5. 【重要变更】注册成功不再计数，因为：
            //    - 业务已保证手机号唯一
            //    - 计数会导致该手机号永远无法再注册：
            // -成功注册时不计数：在注册成功的情况下，
            // 不将该手机号的注册行为计入滑动窗口限流统计中。
            // 这样可以确保成功注册不会影响后续的注册尝试。
            return Result.ok(userRegisterVO);
        } catch (RegisterException e) {
            // 6. 只有注册失败时才计数（防止恶意尝试）
            // 根据异常类型判断是否需要计数，避免对已存在手机号进行错误计数
            if (e.getCode() == RegisterExceptionMsgEnums.PHONE_NUMBER_EXIST.getCode()) {
                // 手机号已存在的情况不计数（因为不是尝试注册，以避免误伤合法用户）
            } else {
                rateLimitService.checkSlidingRateLimit(
                        "register:phone:" + phoneNumber, 3, Duration.ofHours(24));
            }
            return Result.error(e.getMessage());
        }
    }



    @GetMapping("/test")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
//    @PreAuthorize("hasRole('ROLE_USER')")
    public Result<String> test() {
        return Result.ok("test");
    }
}
